#ifndef UHM_UTIL_ID_MESH_HXX
#define UHM_UTIL_ID_MESH_HXX

namespace uhm {

  // Node supports # of dofs...
  class ID_Node_Base_ {
  public:
    
    enum { SCALAR = 1,
           BLOCK } Unit_;

    virtual void set_dof(Int_ dof)=0;
    virtual Int_ get_dof() const=0;
    virtual Int_ dof()=0;

    virtual void set_weight(Int_ weight)=0;
    virtual Int_ get_weight() const=0;
    virtual Int_ weight()=0;

  };
  
  template<Int_ N> class ID_Node_ : public ID_Int_<N>, 
                                    public ID_Node_Base_ {
  protected:
    Int_ _dof, _weight;

  public:

    static const Int_ _Unit = ID_Node_Base_::SCALAR;

    ID_Node_() : ID_Int_<N>(), _dof(0), _weight(0) { } 
    ID_Node_(const ID_Node_ &b) : ID_Int_<N>(b), _dof(b._dof), _weight(b._weight) { }

    virtual ~ID_Node_() { }

    virtual void set_dof(Int_ dof) { _dof = dof; }
    virtual Int_ get_dof() const { return _dof; }
    virtual Int_ dof() { return _dof; }

    // Weight is height of nodes' hierarchy in hpFEM.
    // This will give some guidance for metis to group refined elements 
    // together.
    virtual void set_weight(Int_ weight) { _weight = weight; }
    virtual Int_ get_weight() const { return _weight; }
    virtual Int_ weight() { return _weight; }

    using ID_Int_<N>::operator<;
    using ID_Int_<N>::operator=;
    using ID_Int_<N>::operator==;

    virtual ID_Node_& operator=(const ID_Node_ &b) {
      this->equate(b);
      this->set_dof(b.get_dof());
      this->set_weight(b.get_weight());
      return (*this);
    }
    virtual Bool_ operator<(const ID_Node_ &b) const  {
      return (this->diff(b) < 0);
      //Int_ r_val = this->diff(b);
      //if (r_val) return (r_val <0); 
      //return (this->get_dof() < b.get_dof());
    }
    virtual Bool_ operator==(const ID_Node_ &b) const  {
      return (this->is_equal(b));
      //return (this->is_equal(b) && get_dof() == b.get_dof());
    }
    virtual std::ostream& content(std::ostream& os) const {
      ID_Int_<N>::content(os);
      os << ", Dof(" << get_dof() << ")"
         << ", Weight(" << get_weight() << ")";
      return os;
    }
  };

  template<Int_ N> class ID_Node_H_ : public ID_Node_<N> {
  protected:
  public:

    static const Int_ _Unit = ID_Node_Base_::BLOCK;

    ID_Node_H_() : ID_Node_<N>() { }
    ID_Node_H_(const ID_Node_H_ &b) : ID_Node_<N>(b) { }
  };

  /*
  // Element ID might have communicator later
  class ID_Element_Base_ {
    // abstract one
  };

  template<Int_ N> class ID_Element_ : public ID_Int_<N>,
                                       public ID_Element_Base_ {
  public:
    virtual Bool_ operator<(const ID_Element_ &b) const  {
      Int_ r_val = this->diff(b);
      return (r_val <0);
    }
  };
  */

  // Element ID might have communicator later...
  class ID_Element_Base_ {
    // abstract one 
    virtual void set_part(Int_ part)=0;
    virtual Int_ get_part() const=0;
    virtual Int_ part()=0;

  };

  template<Int_ N> class ID_Element_ : public ID_Int_<N>,
                                       public ID_Element_Base_ {
  protected:
    Int_ _part;

  public:
    ID_Element_() : ID_Int_<N>(), _part(0) { }
    ID_Element_(const ID_Element_ &b) : ID_Int_<N>(b), _part(b._part) { }

    virtual void set_part(Int_ part) { _part = part; }
    virtual Int_ get_part() const { return _part; }
    virtual Int_ part() { return _part; }

    using ID_Int_<N>::operator<;
    using ID_Int_<N>::operator=;
    using ID_Int_<N>::operator==;

    virtual Bool_ operator<(const ID_Element_ &b) const  {
      Int_ r_val = this->diff(b);
      if (r_val) return (r_val <0);
      return (this->get_part() < b.get_part());
    }

    virtual ID_Element_& operator=(const ID_Element_ &b) {
      this->equate(b);
      this->set_part(b.get_part());
      return (*this);
    }

    virtual Bool_ operator==(const ID_Element_ &b) const  {
      return (this->is_equal(b) && get_part() == b.get_part());
    }

    virtual std::ostream& content(std::ostream& os) const {
      ID_Int_<N>::content(os);
      os << ", Part(" << get_part() << ")";
      return os;
    }
  };
}


#endif
